
package provide pdwindow 0.1

namespace eval ::pdwindow:: {
    variable printout_buffer ""
    variable pdwindow_search_index
    variable vu_in_db
    variable vu_out_db
    variable tclentry {}
    variable tclentry_history {"console show"}
    variable history_position 0

    namespace export pdtk_post
    namespace export pdtk_pd_dsp
    namespace export pdtk_pd_meters
    namespace export pdtk_pd_dio
}

# TODO make the background of the text alternate colors based on each
# pdtk_post operation, using the tag-based color command of Tk's text widget

# ------------------------------------------------------------------------------
# pdtk functions for 'pd' to send data to the Pd window

proc ::pdwindow::pdtk_post {message} {
    variable printout_buffer
    # TODO this should be switchable between Pd window and stderr
    if { ! [winfo exists .pdwindow.text]} {
        set printout_buffer "$printout_buffer\n$message"
    } else {
        if {$printout_buffer ne ""} {
            .pdwindow.text insert end "$printout_buffer\n"
            set printout_buffer ""
        }
        .pdwindow.text insert end "$message\n"
        .pdwindow.text yview end
    }
    if {$::stderr} {puts stderr $message}
}

# set the checkbox on the "Compute Audio" menuitem and checkbox
proc ::pdwindow::pdtk_pd_dsp {value} {
    # TODO canvas_startdsp/stopdsp should really send 1 or 0, not "ON" or "OFF"
    if {$value eq "ON"} {
        set ::dsp 1
        .pdwindow.header.dsp configure -background green
    } else {
        set ::dsp 0
        .pdwindow.header.dsp configure -background grey
    }
}

proc ::pdwindow::pdtk_pd_meters {indb outdb inclip outclip} {
#    puts stderr [concat meters $indb $outdb $inclip $outclip]
    set ::pdwindow::vu_in_db $indb
    if {$inclip == 1} {
        .pdwindow.header.cliplabel.in configure -background red
    } else {
        .pdwindow.header.cliplabel.in configure -background grey
    }
    set ::pdwindow::vu_out_db $outdb
    if {$outclip == 1} {
        .pdwindow.header.cliplabel.out configure -background red
    } else {
        .pdwindow.header.cliplabel.out configure -background grey
    }
    
}

proc ::pdwindow::pdtk_pd_dio {red} {
    if {$red == 1} {
        .pdwindow.header.dio configure -background red \
            -highlightbackground red
    } else {
        .pdwindow.header.dio configure -background lightgrey \
            -highlightbackground grey
    }
        
}

#--VU meter procedures---------------------------------------------------------#

proc ::pdwindow::calc_vu_color {position} {
    if {$position > 30} {
        return "#D748E7"
    } elseif {$position > 27} {
        return "#DF3A32"
    } elseif {$position > 25} {
        return "#EEAD54"
    } elseif {$position > 15} {
        return "#E9E448"
    } elseif {$position > 0} {
        return "#65DF37"
    } else {
        return "#6CEFF1"
    }
}

proc ::pdwindow::set_vu {mycanvas name args} {
    if {$name eq "in"} {
        set db $::pdwindow::vu_in_db
    } elseif {$name eq "out"} {
        set db $::pdwindow::vu_out_db
    }
    # TODO figure out the actual math here to map the dB values to a log scale
    set elements [expr $db / 3]
    for {set i 0} {$i<$elements} {incr i} {
        $mycanvas itemconfigure "$name$i" -state normal
    }
    for {set i $elements} {$i<40} {incr i} {
        $mycanvas itemconfigure "$name$i" -state hidden
    }
}

proc ::pdwindow::create_vu {mycanvas name} {
    for {set i 0} {$i<40} {incr i} {
        set x [expr $i * 4]
        $mycanvas create line $x 2 $x 18 -width 3 -tag "$name$i" \
            -fill [calc_vu_color $i] -state hidden
    }
}

#--Tcl entry procs-------------------------------------------------------------#

# copied from ::pd_connect::pd_readsocket, so perhaps it could be merged
proc ::pdwindow::eval_tclentry {} {
    variable tclentry
    variable tclentry_history
    variable history_position 0
    if {[catch {uplevel #0 $tclentry} errorname]} {
        global errorInfo
        switch -regexp -- $errorname { 
            "missing close-brace" {
                pdtk_post [concat [_ "(Tcl) MISSING CLOSE-BRACE '\}': "] $errorInfo]
            } "missing close-bracket" {
                pdtk_post [concat [_ "(Tcl) MISSING CLOSE-BRACKET '\]': "] $errorInfo]
            } "^invalid command name" {
                pdtk_post [concat [_ "(Tcl) INVALID COMMAND NAME: "] $errorInfo]
            } default {
                pdtk_post [concat [_ "(Tcl) UNHANDLED ERROR: "] $errorInfo]
            }
        }
    }    
    lappend tclentry_history $tclentry
    set tclentry {}
}

proc ::pdwindow::get_history {direction} {
    variable tclentry_history
    variable history_position

    incr history_position $direction
    if {$history_position < 0} {set history_position 0}
    if {$history_position > [llength $tclentry_history]} {
        set history_position [llength $tclentry_history]
    }
    .pdwindow.tcl.entry delete 0 end
    .pdwindow.tcl.entry insert 0 \
        [lindex $tclentry_history end-[expr $history_position - 1]]
}

# TODO run [info complete] on tclentry as you type
proc ::pdwindow::validate_tcl {} {
    variable tclentry
    if {[info complete $tclentry]} {
        .pdwindow.tcl.entry configure -background "white"
    } else {
        .pdwindow.tcl.entry configure -background "#FFF0F0"
    }
}

#--create the window-----------------------------------------------------------#

proc ::pdwindow::create_window {} {
    set ::loaded(.pdwindow) 0
    toplevel .pdwindow -class PdWindow
    wm title .pdwindow [_ "Pd window"]
    if {$::windowingsystem eq "x11"} {
        wm minsize .pdwindow 400 75
    } else {
        wm minsize .pdwindow 400 51
    }
    wm geometry .pdwindow =500x450+20+50
    .pdwindow configure -menu .menubar
    .pdwindow configure -background grey
    ::pd_menus::configure_for_pdwindow
    ::pd_bindings::pdwindow_bindings .pdwindow

    frame .pdwindow.header -background grey -borderwidth 1 -relief flat
    pack .pdwindow.header -side top -fill x -padx 5 -ipady 5
    checkbutton .pdwindow.header.meters -variable ::meters \
        -background grey -command {pdsend "pd meters $::meters"}
    pack .pdwindow.header.meters -side left -fill y -anchor w -pady 10
    frame .pdwindow.header.vu -background grey -borderwidth 0
    pack .pdwindow.header.vu -side left
    canvas .pdwindow.header.vu.in -width 150 -height 20 -background "#3F3F3F" \
        -highlightthickness 1 -highlightbackground grey
    create_vu .pdwindow.header.vu.in "in"
    trace add variable ::pdwindow::vu_in_db write \
        "::pdwindow::set_vu .pdwindow.header.vu.in in"
    canvas .pdwindow.header.vu.out -width 150 -height 20 -background "#3F3F3F" \
        -highlightthickness 1 -highlightbackground grey
    create_vu .pdwindow.header.vu.out "out"
    trace add variable ::pdwindow::vu_out_db write \
        "::pdwindow::set_vu .pdwindow.header.vu.out out"
    pack .pdwindow.header.vu.in -side top
    pack .pdwindow.header.vu.out -side top
    frame .pdwindow.header.cliplabel -background grey -borderwidth 0
    pack .pdwindow.header.cliplabel -side left
    label .pdwindow.header.cliplabel.in -text [_ "IN"] -background grey -width 4
    label .pdwindow.header.cliplabel.out -text [_ "OUT"] -background grey -width 4
    pack .pdwindow.header.cliplabel.in .pdwindow.header.cliplabel.out -side top
    checkbutton .pdwindow.header.dsp -text [_ "DSP"] -variable ::dsp \
        -font {$::font_family 18 bold} \
        -background grey -command {pdsend "pd dsp $::dsp"}
    pack .pdwindow.header.dsp -side right -fill y -anchor e -padx 5 -pady 10
# DIO button
    button .pdwindow.header.dio -text [_ "DIO"] -borderwidth 0 -width 4 \
        -highlightbackground grey -highlightthickness 0 -foreground grey \
        -font {$::font_family 14} -command {pdsend "pd audiostatus"} 
    pack .pdwindow.header.dio -side right -fill y -padx 30 -pady 10
# Tcl entry box
    frame .pdwindow.tcl -background grey -borderwidth 0
    pack .pdwindow.tcl -side bottom -fill x
    frame .pdwindow.tcl.pad
    pack .pdwindow.tcl.pad -side right -padx 12
    label .pdwindow.tcl.label -text [_ "Tcl:"] -background grey -anchor e
    pack .pdwindow.tcl.label -side left
    entry .pdwindow.tcl.entry -width 200 -highlightbackground grey \
        -textvariable ::pdwindow::tclentry -font {$::font_family 14}
    bind .pdwindow.tcl.entry <Return> "::pdwindow::eval_tclentry"
    bind .pdwindow.tcl.entry <Up> "::pdwindow::get_history 1"
    bind .pdwindow.tcl.entry <Down> "::pdwindow::get_history -1"
    bind .pdwindow.tcl.entry <KeyRelease> +"::pdwindow::validate_tcl"
    pack .pdwindow.tcl.entry -side left -fill x
# TODO this should use the pd_font_$size created in pd-gui.tcl    
    text .pdwindow.text -relief raised -bd 2 -font {-size 10} \
        -highlightthickness 0 -borderwidth 1 -relief flat \
        -yscrollcommand ".pdwindow.scroll set" -width 60 \
        -undo true -autoseparators true -maxundo -1        
    scrollbar .pdwindow.scroll -command ".pdwindow.text yview"
    pack .pdwindow.scroll -side right -fill y
    pack .pdwindow.text -side right -fill both -expand 1
    raise .pdwindow
    focus .pdwindow.tcl.entry
    set ::loaded(.pdwindow) 1
}
